aviutl2\generic\binding/
project.rs1use crate::load_wide_string;
2
3pub struct ProjectFile<'a> {
5 pub(crate) internal: *mut aviutl2_sys::plugin2::PROJECT_FILE,
6 _marker: std::marker::PhantomData<&'a ()>,
7}
8
9#[derive(thiserror::Error, Debug)]
11pub enum ProjectFileError {
12 #[error("key contains null byte: {0}")]
13 KeyContainsNull(std::ffi::NulError),
14 #[error("data retrieval failed for key {0}")]
15 RetrievalFailed(String),
16 #[error("data length exceeds 4096 bytes, got {0} bytes")]
17 DataTooLarge(usize),
18 #[error("value contains null byte: {0}")]
19 ValueContainsNull(std::ffi::NulError),
20}
21
22impl<'a> ProjectFile<'a> {
23 pub unsafe fn from_raw(raw: *mut aviutl2_sys::plugin2::PROJECT_FILE) -> Self {
29 Self {
30 internal: raw,
31 _marker: std::marker::PhantomData,
32 }
33 }
34
35 pub fn get_param_string(&self, key: &str) -> Result<String, ProjectFileError> {
42 let c_key = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
43 unsafe {
44 let raw_str = ((*self.internal).get_param_string)(c_key.as_ptr() as _);
45 if raw_str.is_null() {
46 return Err(ProjectFileError::RetrievalFailed(key.to_string()));
47 }
48 Ok(std::ffi::CStr::from_ptr(raw_str)
49 .to_string_lossy()
50 .into_owned())
51 }
52 }
53
54 pub fn get_param_binary(&self, key: &str, data: &mut [u8]) -> Result<(), ProjectFileError> {
62 let success = unsafe {
63 let key = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
64 ((*self.internal).get_param_binary)(
65 key.as_ptr() as _,
66 data.as_mut_ptr() as _,
67 data.len() as _,
68 )
69 };
70 if !success {
71 return Err(ProjectFileError::RetrievalFailed(key.to_string()));
72 }
73 Ok(())
74 }
75
76 pub fn set_param_string(&mut self, key: &str, value: &str) -> Result<(), ProjectFileError> {
82 let key_cstr = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
83 let value_cstr =
84 std::ffi::CString::new(value).map_err(ProjectFileError::ValueContainsNull)?;
85 unsafe {
86 ((*self.internal).set_param_string)(key_cstr.as_ptr() as _, value_cstr.as_ptr() as _);
87 }
88 Ok(())
89 }
90
91 pub fn set_param_binary(&mut self, key: &str, data: &[u8]) -> Result<(), ProjectFileError> {
98 if data.len() > 4096 {
99 return Err(ProjectFileError::DataTooLarge(data.len()));
100 }
101 unsafe {
102 let key = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
103 ((*self.internal).set_param_binary)(
104 key.as_ptr() as _,
105 data.as_ptr() as _,
106 data.len() as _,
107 );
108 }
109 Ok(())
110 }
111
112 pub fn clear_params(&mut self) {
114 unsafe { ((*self.internal).clear_params)() }
115 }
116
117 pub fn get_path(&self) -> Option<std::path::PathBuf> {
119 unsafe {
120 let raw_str = ((*self.internal).get_project_file_path)();
121 if raw_str.is_null() {
122 return None;
123 }
124 Some(std::path::PathBuf::from(load_wide_string(raw_str)))
125 }
126 }
127}
128
129#[cfg(feature = "serde")]
130static NAMESPACE: &str = "--aviutl2-rs";
131
132#[cfg(feature = "serde")]
133impl<'a> ProjectFile<'a> {
134 pub fn serialize<T: serde::Serialize>(&mut self, key: &str, value: &T) -> crate::AnyResult<()> {
145 let bytes = rmp_serde::to_vec_named(value)?;
146 let bytes = zstd::encode_all(&bytes[..], 0)?;
147 let num_bytes = bytes.len();
148 self.set_param_string(key, &format!("{NAMESPACE}:serde-zstd-v1:{}", num_bytes))?;
149 for (i, chunk) in bytes.chunks(4096).enumerate() {
150 let chunk_key = format!("{NAMESPACE}:serde-zstd-v1:chunk:{}:{}", key, i);
151 self.set_param_binary(&chunk_key, chunk)?;
152 }
153 Ok(())
154 }
155
156 pub fn deserialize<T: serde::de::DeserializeOwned>(&self, key: &str) -> crate::AnyResult<T> {
158 let header = self.get_param_string(key)?;
159 let header_prefix = format!("{NAMESPACE}:serde-zstd-v1:");
160 let num_bytes = header
161 .strip_prefix(&header_prefix)
162 .ok_or_else(|| anyhow::anyhow!("invalid header for key {}", key))?;
163 let num_bytes: usize = num_bytes.parse()?;
164 if num_bytes == 0 {
165 anyhow::bail!("invalid data length 0 for key {}", key);
166 }
167 let mut bytes = Vec::with_capacity(num_bytes);
168 let mut read_bytes = 0;
169 let mut chunk = vec![0u8; 4096];
170 for i in 0.. {
171 let chunk_key = format!("{NAMESPACE}:serde-zstd-v1:chunk:{}:{}", key, i);
172 let to_read = std::cmp::min(4096, num_bytes - read_bytes);
173 chunk.resize(to_read, 0);
174 match self.get_param_binary(&chunk_key, &mut chunk) {
175 Ok(()) => {
176 bytes.extend_from_slice(&chunk);
177 read_bytes += to_read;
178 if read_bytes >= num_bytes {
179 break;
180 }
181 }
182 Err(_) => break,
183 }
184 }
185 anyhow::ensure!(
186 read_bytes == num_bytes,
187 "incomplete data for key {}, expected {} bytes, got {} bytes",
188 key,
189 num_bytes,
190 read_bytes
191 );
192 let decompressed_bytes = zstd::decode_all(&bytes[..])?;
193 let value: T = rmp_serde::from_slice(&decompressed_bytes)?;
194 Ok(value)
195 }
196}